﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DNA
{
    class DnaGraph
    {
        public DnaGraph()
        {
            StartFeromon = 0.0;
            StartHeat = 0.0;
            VaporRate = 0.0;
            HeatVaporRate = 0.0;
            TotalCost = 0;
            Threshold = int.MaxValue;
            FeromoneToDistribute = 10.0;
        }

        public static double ArcHeuristic(Arc arc)
        {
            return 50.0 / Math.Pow(arc.Cost, 8.0);
        }

        virtual public void Initialize(List<string> nucl)
        {
            _index = new DnaIndex(nucl);

            Nodes = new Dictionary<string, Node>();
            Arcs = new List<Arc>();

            foreach (string item in nucl)
            {
                List<Arc> arcs = GenerateArcList(item);
                foreach (Arc arc in arcs)
                {
                    arc.Heuristic = StartHeat;
                    arc.Heuristic += ArcHeuristic(arc);
                    arc.Feromon = StartFeromon;
                    Arcs.Add(arc);
                }
                Node newNode = new Node(item, arcs);
                Nodes.Add(item, newNode);
            }
        }

        public void Reset()
        {
            foreach (Arc arc in Arcs)
            {
                arc.Heuristic = StartHeat;
                arc.Heuristic += ArcHeuristic(arc);
                arc.Feromon = StartFeromon;
            }
            foreach (Node node in Nodes.Values)
            {
                node.Update();
            }
        }

        public void Update()
        {
            foreach (var arc in Arcs)
            {
                if (arc.changedFlag)
                    arc.Update(VaporRate, HeatVaporRate);
            }
            foreach (var node in Nodes.Values)
            {
                if (node.changedFlag)
                    node.Update();
            }
        }

        public double StartHeat { get; set; }
        public double StartFeromon { get; set; }

        public double VaporRate { get; set; }
        public double HeatVaporRate { get; set; }

        //protected DnaIndex _index;
        protected DnaIndex _index;
        public Dictionary<string,Node> Nodes;
        public List<Arc> Arcs;

        virtual protected List<Arc> GenerateArcList(string str)
        {
            List<Arc> list = new List<Arc>();
            for (int i = 1; i <= str.Length; i++)
            {
                string suffix = str.Substring(i);
                List<string> found = _index.findAllStartingWith(suffix);

                foreach (var suff in found)                
                    list.Add(new Arc(suff,i));                
            }
            return list;
        }

        public class Node
        {
            protected string _Value;
            public string value
            {
                get
                {
                    return _Value;
                }
                protected set
                {
                    _Value = value;
                }
            }
            protected List<Arc> _arcs = null;
            protected double sumOfAppeal;

            public bool changedFlag;

            protected Random rand;

            protected Node()
            {
                rand = new Random();
                changedFlag = false;
            }

            public Node(string str, List<Arc> list)
                : this()
            {
                value = str;
                _arcs = list;
            }

            public Arc GetNext(List<Node> visited)
            {
                double x = rand.NextDouble();
                double visitedSum = 0.0;

                HashSet<string> visitedNodesStrings = new HashSet<string>();

                foreach (Node item in visited)
                {
                    visitedNodesStrings.Add(item.value);
                }

                foreach (Arc arc in _arcs)
                    if (visitedNodesStrings.Contains(arc.NodeString))
                        visitedSum += arc.Appeal;

                double relAppeal = sumOfAppeal - visitedSum;

                x *= relAppeal;

                double sum = 0;
                foreach (var arc in _arcs)
                {
                    if (visitedNodesStrings.Contains(arc.NodeString))
                        continue;

                    sum += arc.Appeal;
                    if (sum > x)
                        return arc;
                }
                return null;
                
            }

            public void Update()
            {
                sumOfAppeal = 0;
                foreach (var arc in _arcs)
                    sumOfAppeal += arc.Appeal;
                changedFlag = false;
            }
        }

        public class Arc
        {
            protected string _node = null;
            public string NodeString
            {
                get
                {
                    return _node;
                }
            }

            protected double _heuristic;
            protected double _feromon;
            protected double _appeal;
            protected bool _changed;
            public bool changedFlag;

            public Arc(string node, int cost)
            {
                Heuristic = 0.0;
                Feromon = 0.0;

                changedFlag = false;

                Cost = cost;
                _node = node;
            }

            public double Heuristic 
            { 
                get
                {
                    return _heuristic;
                }
                set 
                {
                    _heuristic = value; 
                    _changed = true;
                } 
            }
            public double Feromon 
            { 
                get
                {
                    return _feromon;
                } 
                set
                {
                    _feromon = value;
                    _changed = true;
                } 
            }
            public int Cost { get; set; }
            public double Appeal
            {
                get
                {
                    if (_changed)
                    {
                        _appeal = Feromon + Heuristic;
                        _changed = false;
                    }
                    return _appeal;
                    
                }
            }

            public void Update(double feromonVapor, double heatVapor)
            {
                Feromon *= feromonVapor;
                Heuristic *= heatVapor;
                double heur = DnaGraph.ArcHeuristic(this);
                if (heur > Heuristic)
                    Heuristic = heur;
                changedFlag = false;
            }
        }

        public int TotalCost { get; set; }

        public int Threshold { get; set; }

        public double FeromoneToDistribute { get; set; }
    }

    class DnaGraphPosOnly :  DnaGraph
    {
        public DnaGraphPosOnly()
        {

        }

        override protected List<Arc> GenerateArcList(string str)
        {
            List<Arc> list = new List<Arc>();
            string suffix = str.Substring(1);
            List<string> found = _index.findAllStartingWith(suffix);
            foreach (var suff in found)
                list.Add(new Arc(suff, 1));            
            return list;
        }
    }

    class DnaGraphNegOnly : DnaGraph
    {
        public DnaGraphNegOnly()
        {

        }

        public override void Initialize(List<string> nucl)
        {
            List<string> merged = DnaProcessor.Merge(nucl);
            _index = new DnaIndex(nucl);

            Nodes = new Dictionary<string, Node>();
            Arcs = new List<Arc>();

            foreach (string item in nucl)
            {
                int idx = -1;
                string found = null;
                foreach (var mrgd in merged)
                {
                    idx = mrgd.IndexOf(item);
                    if (idx != -1)
                    {
                        found = mrgd;
                        break;
                    }
                }

                string following = null;
                if (found != null && (idx+1+item.Length)<=found.Length)
                    following = found.Substring(idx+1,item.Length);
                int heur = found.Length*1;
                bool add_heur = false;
                if (following != null && following.Length == item.Length)
                    add_heur = true;


                List<Arc> arcs = GenerateArcList(item);
                foreach (Arc arc in arcs)
                {
                    arc.Heuristic = StartHeat;
                    arc.Heuristic += ArcHeuristic(arc);
                    arc.Feromon = StartFeromon;
                    if (add_heur && arc.NodeString == following)
                        arc.Heuristic += heur;
                    Arcs.Add(arc);
                }
                Node newNode = new Node(item, arcs);
                Nodes.Add(item, newNode);
            }
        }
    }
}
